


#include "MainWindow.h"
#include "ui/ui_MainWindow.h"
#include "ui/ui_Inspector.h"

#include "Creature.h"
#include "HumanCreature.h"
#include "HalfHumanCreature.h"
#include "BasicCreature.h"
#include "QuadrupedCreature.h"
#include "Constraint.h"

#include "BulletSimulatorThread.h"
#include "Inspector.h"

#include <QtGui/QClipboard>
#include <QtGui/QTabWidget>
#include <QtCore/QStringList>
#include <QtCore/QDir>
#include <btBulletCollisionCommon.h>



#ifdef UNIX
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#elif MACOSX
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>
#endif
namespace jcrada {

    MainWindow::MainWindow( QWidget* parent )
    : QMainWindow( parent ),
    _inspector( new Inspector( ) ),
    _ui_main_window( new Ui::MainWindow( ) ),
    _ui_inspector( new Ui::Inspector( ) ),
    _simulator_thread( new BulletSimulatorThread( ) ),
    _renderer_thread( new QGLRendererThread( ) ),
    _creature( NULL ) {
    }

    MainWindow::~MainWindow( ) {
        delete _renderer_thread;
        delete _simulator_thread;

        delete _ui_inspector;
        delete _ui_main_window;

        delete _inspector;
    }

    void MainWindow::setupUi( ) {
        getUiMainWindow().setupUi(this);
        getUiInspector().setupUi(&getInspector());

        addDockWidget(Qt::LeftDockWidgetArea, &getInspector());

        getUiInspector().lbl_xml_creature->setText("");
        getUiInspector().lbl_xml_pso->setText("");

        getInspector().setFloating(true);
        getInspector().adjustSize();

        QRect rect = Math::centerInDesktop(getInspector().geometry());
        rect.moveLeft(0);
        getInspector().setGeometry(rect);

        setCentralWidget(&getRendererThread().getRenderer());
        setGeometry(Math::centerInDesktop(geometry()));

        QObject::connect(getUiMainWindow().action_inspector, SIGNAL(triggered()),
                &getInspector(), SLOT(show()), Qt::DirectConnection);
        QObject::connect(getUiInspector().tabs, SIGNAL(currentChanged(int)),
                this, SLOT(changedCurrentTab(int)), Qt::DirectConnection);
        createControlConnections();
        createAppearanceConnections();
        createCreatureConnections();
        createPsoConnections();
        createForcesConnections();

        getRendererThread().getRenderer().setDefaultValues();
//        getSimulatorThread().getSimulator().setDefaultValues();

        getRendererThread().getRenderer().grabKeyboard();

        changedCreature(0);
        changedCurrentTab(getUiInspector().tabs->currentIndex());
    }

    void MainWindow::closeEvent( QCloseEvent* event ) {
        getRendererThread().stop();
        getSimulationThread().stop();
        event->accept();
    }

    void MainWindow::createControlConnections( ) {
        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedMouseMode(int)),
                getUiInspector().cbb_mouse_mode, SLOT(setCurrentIndex(int)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().cbb_mouse_mode, SIGNAL(currentIndexChanged(int)),
                &getRendererThread().getRenderer(), SLOT(setMouseMode(int)),
                Qt::QueuedConnection);

        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedSensitivityFactor(double)),
                getUiInspector().sbx_mouse_sensitivity, SLOT(setValue(double)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().sbx_mouse_sensitivity, SIGNAL(valueChanged(double)),
                &getRendererThread().getRenderer(), SLOT(setSensitivityFactor(double)),
                Qt::QueuedConnection);

        QObject::connect(&getSimulationThread().getSimulator(), SIGNAL(changedTimestepsPerSecond(int)),
                getUiInspector().sbx_timesteps, SLOT(setValue(int)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().sbx_timesteps, SIGNAL(valueChanged(int)),
                &getSimulationThread().getSimulator(), SLOT(setTimestepsPerSecond(int)),
                Qt::QueuedConnection);

        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedFramesPerSecond(int)),
                getUiInspector().sbx_frames, SLOT(setValue(int)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().sbx_frames, SIGNAL(valueChanged(int)),
                &getRendererThread().getRenderer(), SLOT(setFramesPerSecond(int)),
                Qt::QueuedConnection);

        QObject::connect(getUiInspector().cbb_simulation_mode, SIGNAL(currentIndexChanged(int)),
                this, SLOT(changedSimulationMode(int)),
                Qt::QueuedConnection);

        QObject::connect(&getSimulationThread(), SIGNAL(changedEvaluationTimeInSeconds(int)),
                getUiInspector().sbx_evaluation_time, SLOT(setValue(int)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().sbx_evaluation_time, SIGNAL(valueChanged(int)),
                &getSimulationThread(), SLOT(setEvaluationTimeInSeconds(int)),
                Qt::QueuedConnection);

        QObject::connect(&getSimulationThread().getSimulator(), SIGNAL(changedFastForward(bool)),
                getUiInspector().cbx_fast_forward, SLOT(setChecked(bool)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().cbx_fast_forward, SIGNAL(toggled(bool)),
                &getSimulationThread().getSimulator(), SLOT(setFastForward(bool)),
                Qt::QueuedConnection);

        QObject::connect(&getSimulationThread().getSimulator(), SIGNAL(changedGravity(double)),
                getUiInspector().sbx_gravity, SLOT(setValue(double)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().sbx_gravity, SIGNAL(valueChanged(double)),
                &getSimulationThread().getSimulator(), SLOT(setGravity(double)),
                Qt::QueuedConnection);

        QObject::connect(getUiInspector().btn_step, SIGNAL(clicked()),
                &getSimulationThread(), SLOT(step()),
                Qt::QueuedConnection);

        //        QObject::connect(getUiInspector().btn_play, SIGNAL(clicked()),
        //                this, SLOT(play()),
        //                Qt::DirectConnection);
        //        QObject::connect(getUiInspector().btn_pause, SIGNAL(clicked()),
        //                this, SLOT(pause()),
        //                Qt::DirectConnection);
        ////        QObject::connect(&getSimulatorThread().getSimulator(), SIGNAL(changedPlay(bool)),
        ////                getUiInspector().btn_pause, SLOT(setEnabled(bool)),
        ////                Qt::DirectConnection);

        QObject::connect(getUiInspector().btn_play_pause, SIGNAL(clicked()),
                this, SLOT(togglePlay()),
                Qt::DirectConnection);
        QObject::connect(&getSimulationThread().getSimulator(), SIGNAL(changedPlay(bool)),
                this, SLOT(changedPlay(bool)),
                Qt::DirectConnection);
    }

    void MainWindow::createAppearanceConnections( ) {
        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedRenderable(bool)),
                getUiInspector().cbx_render, SLOT(setChecked(bool)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().cbx_render, SIGNAL(toggled(bool)),
                &getRendererThread().getRenderer(), SLOT(setRenderable(bool)),
                Qt::QueuedConnection);

        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedShowAxis(bool)),
                getUiInspector().cbx_axis, SLOT(setChecked(bool)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().cbx_axis, SIGNAL(toggled(bool)),
                &getRendererThread().getRenderer(), SLOT(setShowAxis(bool)),
                Qt::QueuedConnection);

        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedShowCenterOfMass(bool)),
                getUiInspector().cbx_center_of_mass, SLOT(setChecked(bool)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().cbx_center_of_mass, SIGNAL(toggled(bool)),
                &getRendererThread().getRenderer(), SLOT(setShowCenterOfMass(bool)),
                Qt::QueuedConnection);

        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedShowConnectionPoints(bool)),
                getUiInspector().cbx_connection_points, SLOT(setChecked(bool)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().cbx_connection_points, SIGNAL(toggled(bool)),
                &getRendererThread().getRenderer(), SLOT(setShowConnectionPoints(bool)),
                Qt::QueuedConnection);

        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedShowBoundingSpheres(bool)),
                getUiInspector().cbx_bounding_spheres, SLOT(setChecked(bool)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().cbx_bounding_spheres, SIGNAL(toggled(bool)),
                &getRendererThread().getRenderer(), SLOT(setShowBoundingSpheres(bool)),
                Qt::QueuedConnection);

        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedFollowCreature(bool)),
                getUiInspector().cbx_follow_creature, SLOT(setChecked(bool)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().cbx_follow_creature, SIGNAL(toggled(bool)),
                &getRendererThread().getRenderer(), SLOT(setFollowCreature(bool)),
                Qt::QueuedConnection);

        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedSolidDraw(bool)),
                getUiInspector().cbx_solid_draw, SLOT(setChecked(bool)),
                Qt::QueuedConnection);
        QObject::connect(getUiInspector().cbx_solid_draw, SIGNAL(toggled(bool)),
                &getRendererThread().getRenderer(), SLOT(setSolidDraw(bool)),
                Qt::QueuedConnection);
    }

    void MainWindow::createCreatureConnections( ) {
        QObject::connect(getUiInspector().cbb_creature, SIGNAL(currentIndexChanged(int)),
                this, SLOT(changedCreature(int)), Qt::QueuedConnection);

        QObject::connect(getUiInspector().sbx_impulse_explicit, SIGNAL(valueChanged(double)),
                this, SLOT(setCreatureImpulse(double)), Qt::DirectConnection);

        QObject::connect(getUiInspector().btn_create_creature, SIGNAL(clicked()),
                this, SLOT(createCreature()), Qt::QueuedConnection);

        QObject::connect(getUiInspector().btn_creature_add, SIGNAL(clicked()),
                this, SLOT(addCreatureToWorld()), Qt::QueuedConnection);

        QObject::connect(getUiInspector().cbx_behavior_enable, SIGNAL(toggled(bool)),
                &getSimulationThread(), SLOT(setBehaviorEnabled(bool)), Qt::QueuedConnection);
        QObject::connect(&getSimulationThread(), SIGNAL(changedBehaviorEnabled(bool)),
                getUiInspector().cbx_behavior_enable, SLOT(setChecked(bool)), Qt::QueuedConnection);

        QObject::connect(getUiInspector().cbx_behavior_on_ground, SIGNAL(toggled(bool)),
                &getSimulationThread(), SLOT(setBehaviorOnGround(bool)), Qt::QueuedConnection);
        QObject::connect(&getSimulationThread(), SIGNAL(changedBehaviorOnGround(bool)),
                getUiInspector().cbx_behavior_on_ground, SLOT(setChecked(bool)), Qt::QueuedConnection);

        QObject::connect(getUiInspector().btn_creature_to_xml, SIGNAL(clicked()),
                this, SLOT(creatureToClipboard()), Qt::DirectConnection);

        QObject::connect(getUiInspector().btn_creature_from_xml, SIGNAL(clicked()),
                this, SLOT(creatureFromClipboard()), Qt::DirectConnection);

    }

    void MainWindow::createPsoConnections( ) {
        QObject::connect(getUiInspector().btn_set_pso, SIGNAL(clicked()),
                this, SLOT(setPso()), Qt::DirectConnection);
        QObject::connect(getUiInspector().btn_reset_pso, SIGNAL(clicked()),
                this, SLOT(resetPso()), Qt::DirectConnection);
        QObject::connect(getUiInspector().btn_pso_to_xml, SIGNAL(clicked()),
                this, SLOT(psoToClipboard()), Qt::DirectConnection);

        QObject::connect(getUiInspector().btn_pso_from_xml, SIGNAL(clicked()),
                this, SLOT(psoFromClipboard()), Qt::DirectConnection);
    }

    void MainWindow::createForcesConnections( ) {
        QObject::connect(&getSimulationThread().getSimulator(), SIGNAL(creatureAdded()),
                this, SLOT(onCreatureAdded()), Qt::DirectConnection);
        //        QObject::connect(&getRendererThread().getRenderer(), SIGNAL(changedBodySelection()),
        //                this, SLOT(onChangedBodySelection()), Qt::QueuedConnection);

        QObject::connect(getUiInspector().sbx_force_x, SIGNAL(valueChanged(double)),
                this, SLOT(forceChanged(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_force_y, SIGNAL(valueChanged(double)),
                this, SLOT(forceChanged(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_force_z, SIGNAL(valueChanged(double)),
                this, SLOT(forceChanged(double)), Qt::DirectConnection);

        QObject::connect(getUiInspector().lst_bodies, SIGNAL(itemSelectionChanged()),
                this, SLOT(onBodyItemSelectionChanged()), Qt::DirectConnection);

        QObject::connect(getUiInspector().btn_duplicate_forces, SIGNAL(clicked()),
                this, SLOT(duplicateForces()), Qt::DirectConnection);

        QObject::connect(getUiInspector().btn_apply_central_force, SIGNAL(clicked()),
                this, SLOT(applyCentralForce()), Qt::DirectConnection);
        QObject::connect(getUiInspector().btn_apply_central_torque, SIGNAL(clicked()),
                this, SLOT(applyCentralTorque()), Qt::DirectConnection);

        QObject::connect(getUiInspector().btn_apply_impulse_force, SIGNAL(clicked()),
                this, SLOT(applyImpulseForce()), Qt::DirectConnection);
        QObject::connect(getUiInspector().btn_apply_impulse_torque, SIGNAL(clicked()),
                this, SLOT(applyImpulseTorque()), Qt::DirectConnection);

        QObject::connect(getUiInspector().btn_clear_forces, SIGNAL(clicked()),
                this, SLOT(clearForces()), Qt::DirectConnection);

        //Constraints
        QObject::connect(getUiInspector().lst_constraints, SIGNAL(itemSelectionChanged()),
                this, SLOT(onConstraintItemSelectionChanged()), Qt::DirectConnection);

        QObject::connect(getUiInspector().rbt_motor_x, SIGNAL(clicked()),
                this, SLOT(changedConstraintMotor()), Qt::DirectConnection);
        QObject::connect(getUiInspector().rbt_motor_y, SIGNAL(clicked()),
                this, SLOT(changedConstraintMotor()), Qt::DirectConnection);
        QObject::connect(getUiInspector().rbt_motor_z, SIGNAL(clicked()),
                this, SLOT(changedConstraintMotor()), Qt::DirectConnection);

        QObject::connect(getUiInspector().cbx_motor_enable, SIGNAL(clicked(bool)),
                this, SLOT(changedConstraintEnable(bool)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_erp, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintErp(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_accumulated_impulse, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintAccumulatedImpulse(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_bounce, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintBounce(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_current_limit, SIGNAL(valueChanged(int)),
                this, SLOT(changedConstraintCurrentLimit(int)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_current_limit_error, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintCurrentLimitError(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_damping, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintDamping(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_hi_limit, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintHiLimit(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_limit_softness, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintLimitSoftness(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_lo_limit, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintLoLimit(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_max_limit_force, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintMaxLimitForce(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_max_motor_force, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintMaxMotorForce(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_target_velocity, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintTargetVelocity(double)), Qt::DirectConnection);

        QObject::connect(getUiInspector().sbx_lower_angular_dof_x, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintLowerAngularLimitX(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_lower_angular_dof_y, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintLowerAngularLimitY(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_lower_angular_dof_z, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintLowerAngularLimitZ(double)), Qt::DirectConnection);

        QObject::connect(getUiInspector().sbx_upper_angular_dof_x, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintUpperAngularLimitX(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_upper_angular_dof_y, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintUpperAngularLimitY(double)), Qt::DirectConnection);
        QObject::connect(getUiInspector().sbx_upper_angular_dof_z, SIGNAL(valueChanged(double)),
                this, SLOT(changedConstraintUpperAngularLimitZ(double)), Qt::DirectConnection);


    }
    //--------------------------------------------------------------------------
    //SLOTS
    //--------------------------------------------------------------------------


    //CONTROL

    void MainWindow::changedSimulationMode( int index ) {
        (void) index;
        pause();
    }

    void MainWindow::togglePlay( ) {
        switch (getUiInspector().cbb_simulation_mode->currentIndex()) {
            case 0:changedPlay(!getSimulationThread().getSimulator().isPlaying());
                break;
            case 1:
//                changedPlay(getNeuralThread().isStopped());
                break;
            default:
                BDEBUG(TO_STRING(getUiInspector().cbb_simulation_mode->currentIndex()));
                assert(0);
        }
    }

    void MainWindow::changedPlay( bool play ) {
        QIcon icon;
        icon.addPixmap(QPixmap(QString::fromUtf8(
                play ? ":/icon/res/icons/player_pause.png" : ":/icon/res/icons/player_play.png")),
                QIcon::Normal, QIcon::On);
        getUiInspector().btn_play_pause->setIcon(icon);
        if (play) {
            this->play();
        } else {
            this->pause();
        }
    }

    void MainWindow::play( ) {
        switch (getUiInspector().cbb_simulation_mode->currentIndex()) {
            case 0:
                if (getSimulationThread().getSimulator().isPlaying()) {
                    break;
                }
                if (getUiInspector().cbx_evaluation_time->isChecked()) {
                    getSimulationThread().setSimulationMode(BulletSimulatorThread::SM_EVALUATION);
                    getSimulationThread().setEvaluationTimeInSeconds(getUiInspector().sbx_evaluation_time->value());
                } else {
                    getSimulationThread().setSimulationMode(BulletSimulatorThread::SM_FREE);
                }
                getSimulationThread().getSimulator().play(true);
                break;
            case 1:
//                getSimulatorThread().setSimulationMode(BulletSimulatorThread::SM_EVALUATION);
//                DEBUGM;
//                getNeuralThread().getModel().create();
//                DEBUGM;
//                getNeuralThread().getModel().prepare();
//                DEBUGM;
//                getNeuralThread().start();
                break;
        }

    }

    void MainWindow::pause( ) {
        getSimulationThread().getSimulator().play(false);
//        getNeuralThread().stop(true);
        //        getSimulatorThread().wait();
    }

    void MainWindow::changedCurrentTab( int tab ) {
        getUiInspector().lbl_current_tab->setText(
                getUiInspector().tabs->tabToolTip(tab));
    }

    void MainWindow::hideXmlMessages( ) {

        getUiInspector().lbl_xml_creature->setText("");
        //        getUiInspector().lbl_xml_creature->setVisible(false);

        getUiInspector().lbl_xml_pso->setText("");
        //        getUiInspector().lbl_xml_pso->setVisible(false);
    }

    int MainWindow::main( int argc, char** argv ) {
        QApplication app(argc,argv);
        MainWindow window;
        window.setupUi();
        window.show();
        return app.exec();
    }

}
